package com.lvcoding.auth2.auth.shizhan.config;

import com.lvcoding.auth2.auth.shizhan.constant.ShiZhanConstant;
import com.lvcoding.auth2.auth.shizhan.entity.SysUser;
import com.lvcoding.auth2.auth.shizhan.grant.CustomTokenGranter;
import com.lvcoding.auth2.auth.shizhan.security.CustomWebResponseExceptionTranslator;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wuyanshen
 * @description 授权服务器配置
 */
@RequiredArgsConstructor
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    private final AuthenticationManager authenticationManager;
    private final PasswordEncoder passwordEncoder;
    private final RedisConnectionFactory redisConnectionFactory;
    private final DataSource dataSource;

    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
        jdbcClientDetailsService.setSelectClientDetailsSql(ShiZhanConstant.DEFAULT_SELECT_STATEMENT);
        return jdbcClientDetailsService;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints.authenticationManager(authenticationManager) // 密码模式必须配置authenticationManager，否则会在请求时报错
                // .userDetailsService(userDetailsService) // 刷新token必须配置userDetailsService
                // token增强
                .tokenEnhancer(tokenEnhancer())
                // redis存token
                .tokenStore(tokenStore())
                // token生成配置
                .tokenServices(tokenServices())
                // 自定义认证异常返回值
                .exceptionTranslator(new CustomWebResponseExceptionTranslator());

        // 自定义令牌提供者
        setTokenGranter(endpoints);
    }

    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setTokenEnhancer(tokenEnhancer());
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setReuseRefreshToken(true);

        return defaultTokenServices;
    }

    /**
     * 自定义令牌提供者
     */
    private void setTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
        // 原来的授权提供者
        TokenGranter tokenGranter = endpoints.getTokenGranter();
        // 自定义授权提供者
        CustomTokenGranter customTokenGranter = new CustomTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());

        List<TokenGranter> granterList = new ArrayList<>();
        granterList.add(tokenGranter);
        granterList.add(customTokenGranter);
        CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList);
        endpoints.tokenGranter(compositeTokenGranter);
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            String name = authentication.getUserAuthentication().getName();
            Map<String, Object> additionalInformation = new HashMap<>();
            additionalInformation.put("username", name);
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
            return accessToken;
        };
    }

    @Bean
    public TokenStore tokenStore() {
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        redisTokenStore.setPrefix("shizhan:");
        return redisTokenStore;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {


        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                // 自定义client的过滤器配置后，就不需要配置下面的允许表单认证了，即使不配置也可以表单认证
                .allowFormAuthenticationForClients(); // 允许客户端模式使用表单认证

    }

}
